package com.xiaoyu.lanling.feature.newtips.data

import `in`.srain.cube.app.init.InitializableAsync
import `in`.srain.cube.request.JsonData
import `in`.srain.cube.util.CLog
import android.content.Context
import com.xiaoyu.base.data.UserData
import com.xiaoyu.base.data.UserKVConfigData
import com.xiaoyu.lanling.feature.newtips.datamodels.NewTipItem
import com.xiaoyu.lanling.feature.newtips.datamodels.NewTipType
import com.xiaoyu.lanling.feature.newtips.datamodels.NewTipType.NewTipTypeDef
import com.xiaoyu.lanling.feature.newtips.datamodels.NewTipType.fromString
import com.xiaoyu.lanling.feature.newtips.datamodels.NewTipType.getClass
import java.util.*
import java.util.concurrent.ConcurrentHashMap

class NewTipData private constructor() : InitializableAsync {

    private val mItemByType: MutableMap<String, NewTipItem> = ConcurrentHashMap()

    private fun update(@NewTipTypeDef type: String, num: Int) {
        var num = num
        num = 0.coerceAtLeast(num)
        var item = mItemByType[type]
        var hasNewTip = false
        if (item == null) {
            synchronized(mItemByType) {
                item = mItemByType[type]
                if (item == null) {
                    hasNewTip = true
                    mItemByType[type] = NewTipItem(type)
                }
            }
        }
        mItemByType[type]?.let {
            if (it.updateNum(num) || hasNewTip) {
                postStickyEvent(it)
                save()
            }
        }
    }

    private fun postStickyEvent(item: NewTipItem) {
        try {
            val constructor = getClass(item.type)?.getDeclaredConstructor(item.javaClass)
            val event = constructor?.newInstance(item)
            event?.postSticky()
        } catch (e: Exception) {
            CLog.e(TAG, "error when create event: %s", item)
            e.printStackTrace()
        }
    }

    fun onRead(@NewTipTypeDef type: String) {
        update(type, 0)
    }

    fun onRead(tipItem: NewTipItem) {
        onRead(tipItem.type)
    }

    fun clear() {
        mItemByType.clear()
    }

    fun processJsonData(jsonData: JsonData) {
        for (itemJson in jsonData.toList()) {
            val typeString: String = itemJson.optString("type")
            val num: Int = itemJson.optInt("num")
            @NewTipTypeDef val type = fromString(typeString)
            type?.let { update(it, num) }
        }
    }

    fun updateNewTip(item: NewTipItem) {
        @NewTipTypeDef val type = item.type
        if (type != null) {
            update(type, item.num)
        }
    }

    @Synchronized
    fun save() {
        val itemsByType: Map<String, NewTipItem> = HashMap(mItemByType)
        val listJson: JsonData = JsonData.newList()
        for ((typeString, value) in itemsByType) {
            val num = value.num
            val itemJson: JsonData = JsonData.newMap()
            itemJson.put("type", typeString)
            itemJson.put("num", num)
            listJson.put(itemJson)
        }
        UserData.getInstance().saveUserKV(UserKVConfigData.KEY_NEW_TIP_DATA, listJson)
    }

    fun updateMainChatCount(newTipCount: Int) {
        update(NewTipType.MAIN_CHAT_COUNT, newTipCount)
    }

    fun updateMomentNoticeCount(num: Int) {
        update(NewTipType.MOMENT_NOTICE_COUNT, num)
    }

    override fun initiateAsync(context: Context) {
        reload()
    }

    fun reload() {
        val jsonData: JsonData = UserData.getInstance().getUserKV(UserKVConfigData.KEY_NEW_TIP_DATA)
        processJsonData(jsonData)
    }

    companion object {
        private val TAG = NewTipData::class.java.simpleName
        val instance = NewTipData()
    }
}